using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;

public static class ExtensionMethonds
{
    public static void Log(this object obj, object log)
    {
        JLog.Log(log);
    }

    public static void Log(this object obj, string msg, params object[] args)
    {
        JLog.Log(string.Format(msg, args));
    }

    public static void LogColor(this object obj, object log, LogColorEnum color = LogColorEnum.Green)
    {
        JLog.LogColor(log, color);
    }

    public static void LogColor(this object obj, string msg, LogColorEnum color, params object[] args)
    {
        JLog.LogColor(string.Format(msg, args));
    }

    // public static void Trace(this object obj, object log) {
    //     PEUtils.JLog.Trace(log);
    // }
    // public static void Trace(this object obj, string msg, params object[] args) {
    //     PEUtils.JLog.Trace(string.Format(msg, args));
    // }
    public static void LogWarning(this object obj, object log)
    {
        JLog.LogWarning(log);
    }

    public static void LogWarning(this object obj, string msg, params object[] args)
    {
        JLog.LogWarning(string.Format(msg, args));
    }

    public static void LogError(this object obj, object log)
    {
        JLog.LogError(log);
    }

    public static void LogError(this object obj, string msg, params object[] args)
    {
        JLog.LogError(string.Format(msg, args));
    }
}


    public static class JLog
    {
        class UnityLogger : ILogger
        {
            Type type = Type.GetType("UnityEngine.Debug, UnityEngine");

            public void Log(string msg, LogColorEnum color = LogColorEnum.None)
            {
                if (color != LogColorEnum.None)
                {
                    msg = ColorUnityLog(msg, color);
                }

                type.GetMethod("Log", new Type[] {typeof(object)}).Invoke(null, new object[] {msg});
            }

            public void LogWarning(string msg)
            {
                type.GetMethod("LogWarning", new Type[] {typeof(object)}).Invoke(null, new object[] {msg});
            }

            public void LogError(string msg)
            {
                type.GetMethod("LogError", new Type[] {typeof(object)}).Invoke(null, new object[] {msg});
            }

            private string ColorUnityLog(string msg, LogColorEnum color)
            {
                switch (color)
                {
                    case LogColorEnum.Red:
                        msg = string.Format("<color=#FF0000>{0}</color>", msg);
                        break;
                    case LogColorEnum.Green:
                        msg = string.Format("<color=#00FF00>{0}</color>", msg);
                        break;
                    case LogColorEnum.Blue:
                        msg = string.Format("<color=#0000FF>{0}</color>", msg);
                        break;
                    case LogColorEnum.Cyan:
                        msg = string.Format("<color=#00FFFF>{0}</color>", msg);
                        break;
                    case LogColorEnum.Magenta:
                        msg = string.Format("<color=#FF00FF>{0}</color>", msg);
                        break;
                    case LogColorEnum.Yellow:
                        msg = string.Format("<color=#FFFF00>{0}</color>", msg);
                        break;
                    case LogColorEnum.None:
                    default:
                        break;
                }

                return msg;
            }
        }

        class ConsoleLogger : ILogger
        {
            public void Log(string msg, LogColorEnum color = LogColorEnum.None)
            {
                WriteConsoleLog(msg, color);
            }

            public void LogWarning(string msg)
            {
                WriteConsoleLog(msg, LogColorEnum.Yellow);
            }

            public void LogError(string msg)
            {
                WriteConsoleLog(msg, LogColorEnum.Red);
            }

            private void WriteConsoleLog(string msg, LogColorEnum color)
            {
                switch (color)
                {
                    case LogColorEnum.Red:
                        Console.ForegroundColor = ConsoleColor.DarkRed;
                        Console.WriteLine(msg);
                        Console.ForegroundColor = ConsoleColor.Gray;
                        break;
                    case LogColorEnum.Green:
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine(msg);
                        Console.ForegroundColor = ConsoleColor.Gray;
                        break;
                    case LogColorEnum.Blue:
                        Console.ForegroundColor = ConsoleColor.Blue;
                        Console.WriteLine(msg);
                        Console.ForegroundColor = ConsoleColor.Gray;
                        break;
                    case LogColorEnum.Cyan:
                        Console.ForegroundColor = ConsoleColor.Cyan;
                        Console.WriteLine(msg);
                        Console.ForegroundColor = ConsoleColor.Gray;
                        break;
                    case LogColorEnum.Magenta:
                        Console.ForegroundColor = ConsoleColor.Magenta;
                        Console.WriteLine(msg);
                        Console.ForegroundColor = ConsoleColor.Gray;
                        break;
                    case LogColorEnum.Yellow:
                        Console.ForegroundColor = ConsoleColor.DarkYellow;
                        Console.WriteLine(msg);
                        Console.ForegroundColor = ConsoleColor.Gray;
                        break;
                    case LogColorEnum.None:
                    default:
                        Console.WriteLine(msg);
                        break;
                }
            }
        }

        private static ILogger logger;
        public static LogConfig cfg;
        private static StreamWriter LogFileWriter = null;
        private const string logLock = "PELogLock";
#if UNITY_64
        [UnityEngine. RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.BeforeSceneLoad)]

#endif
        public static void InitSettings(LogConfig cfg )
        {
            if (cfg == null)
            {
                cfg = new LogConfig();
            }

            JLog.cfg = cfg;

            if (cfg.loggerEnum == LoggerType.Server)
            {
                logger = new ConsoleLogger();
            }
            else
            {
                logger = new UnityLogger();
            }

            if (cfg.enableSave == false)
            {
                return;
            }

            if (cfg.enableCover)
            {
                string path = cfg.savePath + cfg.saveName;
                try
                {
                    if (Directory.Exists(cfg.savePath))
                    {
                        if (File.Exists(path))
                        {
                            File.Delete(path);
                        }
                    }
                    else
                    {
                        Directory.CreateDirectory(cfg.savePath);
                    }

                    LogFileWriter = File.AppendText(path);
                    LogFileWriter.AutoFlush = true;
                }
                catch (Exception)
                {
                    LogFileWriter = null;
                }
            }
            else
            {
                string prefix = DateTime.Now.ToString("yyyyMMdd@HH-mm-ss");
                string path = cfg.savePath + prefix + cfg.saveName;
                try
                {
                    if (Directory.Exists(cfg.savePath) == false)
                    {
                        Directory.CreateDirectory(cfg.savePath);
                    }

                    LogFileWriter = File.AppendText(path);
                    LogFileWriter.AutoFlush = true;
                }
                catch (Exception )
                {
                    LogFileWriter = null;
                }
            }
        }

        /// <summary>
        /// 常规支持Format的日志
        /// </summary>
        public static void Log(string msg, params object[] args)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            msg = DecorateLog(string.Format(msg, args));
            lock (logLock)
            {
                logger.Log(msg);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[L]{0}", msg));
                }
            }
        }

        public static void Log(object obj)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            string msg = DecorateLog(obj.ToString());
            lock (logLock)
            {
                logger.Log(msg);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[L]{0}", msg));
                }
            }
        }

        /// <summary>
        /// 支持自定义颜色的日志
        /// </summary>
        public static void LogColor(LogColorEnum color, string msg, params object[] args)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            msg = DecorateLog(string.Format(msg, args));
            lock (logLock)
            {
                logger.Log(msg, color);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[L]{0}", msg));
                }
            }
        }

        public static void LogColor(object obj, LogColorEnum color = LogColorEnum.Green)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            string msg = DecorateLog(obj.ToString());
            lock (logLock)
            {
                logger.Log(msg, color);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[L]{0}", msg));
                }
            }
        }

        /// <summary>
        /// 支持Format的堆栈日志
        /// </summary>:
        private static void Trace(string msg, params object[] args)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            msg = DecorateLog(string.Format(msg, args), cfg.enableTrace);
            lock (logLock)
            {
                logger.Log(msg, LogColorEnum.Magenta);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[T]{0}", msg));
                }
            }
        }

        private static void Trace(object obj)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            string msg = DecorateLog(obj.ToString(), cfg.enableTrace);
            lock (logLock)
            {
                logger.Log(msg, LogColorEnum.Magenta);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[T]{0}", msg));
                }
            }
        }

        /// <summary>
        /// 警告日志（黄色）
        /// </summary>
        public static void LogWarning(string msg, params object[] args)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            msg = DecorateLog(string.Format(msg, args));
            lock (logLock)
            {
                logger.LogWarning(msg);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[W]{0}", msg));
                }
            }
        }

        public static void LogWarning(object obj)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            string msg = DecorateLog(obj.ToString());
            lock (logLock)
            {
                logger.LogWarning(msg);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[W]{0}", msg));
                }
            }
        }

        /// <summary>
        /// 错误日志（红色，输出堆栈）
        /// </summary>
        public static void LogError(string msg, params object[] args)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            msg = DecorateLog(string.Format(msg, args), cfg.enableTrace);
            
            lock (logLock)
            {
                logger.LogError(msg);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[E]{0}", msg));
                }
            }
        }

        public static void LogError(object obj)
        {
            if (cfg.enableLog == false)
            {
                return;
            }

            string msg = DecorateLog(obj.ToString(), cfg.enableTrace);
            lock (logLock)
            {
                logger.LogError(msg);
                if (cfg.enableSave)
                {
                    WriteToFile(string.Format("[E]{0}", msg));
                }
            }
        }

        //Tool
        private static string DecorateLog(string msg, bool isTrace = false)
        {
            StringBuilder sb = new StringBuilder( 100);
            if (cfg.enableTime)
            {
                sb.AppendFormat(" {0}{1}{2}", cfg.logPrefix,
                    $"[Time:{DateTime.Now.ToString("hh:mm:ss.fff")}]","");
            }

            if (cfg.enableThreadID)
            {
                sb.AppendFormat(" {0}", GetThreadID());
            }

            sb.AppendFormat(" {0} {1}", cfg.logSeparate, msg);
            if (isTrace)
            {
                sb.AppendFormat("\nStackTrace:{0}", GetLogTrace());
            }

            return sb.ToString();
        }

        private static string GetThreadID()
        {
            return string.Format(" ThreadID:{0}", Thread.CurrentThread.ManagedThreadId);
        }

        private static string GetLogTrace()
        {
            StackTrace st = new StackTrace(3, true); //跳跃3帧
            string traceInfo = "";
            for (int i = 0; i < st.FrameCount; i++)
            {
                StackFrame sf = st.GetFrame(i);
                traceInfo += string.Format("\n    {0}::{1} Line:{2}", sf.GetFileName(), sf.GetMethod(),
                    sf.GetFileLineNumber());
            }

            return traceInfo;
        }

        private static void WriteToFile(string msg)
        {
            if (cfg.enableSave && LogFileWriter != null)
            {
                try
                {
                    LogFileWriter.WriteLine(msg);
                }
                catch (Exception)
                {
                    LogFileWriter = null;
                    return;
                }
            }
        }

        //打印数组数据For Debug
        public static void PrintBytesArray(byte[] bytes, string prefix, Action<string> printer = null)
        {
            string str = prefix + "->\n";
            for (int i = 0; i < bytes.Length; i++)
            {
                if (i % 10 == 0)
                {
                    str += bytes[i] + "\n";
                }

                str += bytes[i] + " ";
            }

            if (printer != null)
            {
                printer(str);
            }
            else
            {
                Log(str);
            }
        }
    }
